/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/
/* 
This file is cotains the implementation for the Color Conversion module. 
It contains the implementation of the function ConvertPixelColorToGrayscale(), 
used to convert the color value of a pixel to grayscale. 

This File was written as part of HW assignment 1 in the course Introduction
To Systems Programming at Tel-Aviv University's School of Electrical 
Engineering, Winter 2011, by Amnon Drory.
*/
/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include "IspImageAnalysis.h"

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

/**
 * IsMarkedPixel() checks to see if a pixel is a special, 'marked' pixel.
 * Accepts an IspImage and a location (y,x) in it.
 * return TRUE if the pixel is marked, FALSE otherwise.
 */
static BOOL IsMarkedPixel( IspImage I, int y, int x );

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#define NEIGHBORHOOD_RADIUS_FOR_RECOGNIZING_MARKED_PIXEL 1

#define INT_DIVIDE_AND_ROUND( A, B ) ( (2*(A) + (B) )/( 2*(B) ))
/* The macro INT_DIVIDE_AND_ROUND uses only integer operations to 
	rounded result of dividing integer A by integer B. It is based
	on the mathematical equivalence: " round(A/B) == floor( (A+0.5) / B ), "
	and on the fact that integer division performs the mathematical operation
	floor(A/B). */

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

Byte_t ConvertPixelColorToGrayscale( Byte_t Red, Byte_t Green, Byte_t Blue )
{
	/* This function calculates the grayscale level of a pixel 
	   using the following formula:
	   Grayscale = Round( ( 0.299 * R ) + ( 0.587 * G ) + ( 0.114 * B ) )
	   ( where R means Red, G means Green, B means Blue ). */

	const unsigned int Denominator = 1000;
	const unsigned int RedFactorNumerator = 299;
	const unsigned int GreenFactorNumerator = 587;
	const unsigned int BlueFactorNumerator = 114;

	unsigned int Numerator = 
					( RedFactorNumerator   * (unsigned int)Red ) + 
					( GreenFactorNumerator * (unsigned int)Green ) +
					( BlueFactorNumerator  * (unsigned int)Blue );

	unsigned int Grayscale = INT_DIVIDE_AND_ROUND( Numerator, Denominator );

	return (Byte_t)Grayscale;
}

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

BOOL BoxContainsAFace( 
	IspImage I, 
	int BoxTop, 
	int BoxLeft, 
	int HeightOfBox, 
	int WidthOfBox,
	int ImageHeight,
	int ImageWidth )
{
	// This function works with special images in which the results of 
	// a face-detector algorithm have been marked. A box is marked as 
	// containing a face by marking the 4 pixels on its corners in a 
	// special way (see IsMarkedPixel() for details ). There's a small
	// chance of the marking being lost when converting the image from
	// RGB to grayscale, so this function returns TRUE even if one 
	// of the corners is not marked.

	#define NUM_OF_CORNERS 4
	#define MINIMUM_REQUIRED_MARKED_CORNERS 3

	int CornerInd;	
	int NumOfMarkedCorners;

	int BoxBottom = ( BoxTop + HeightOfBox );
	int BoxRight = (BoxLeft+WidthOfBox);
	
	int Corners_y[4] = { 
		/* Top Left     */ BoxTop, 
		/* Top Right    */ BoxTop, 
		/* Bottom Left  */ BoxBottom, 
		/* Bottom Right */ BoxBottom
	};
	
	int Corners_x[4] = { 
		/* Top Left     */ BoxLeft, 
		/* Top Right    */ BoxRight,
		/* Bottom Left  */ BoxLeft, 
		/* Bottom Right */ BoxRight 
	};

	// If box is too close to the edges of the image, or is not fully contained in the image, 
	// return FALSE.
	int SmallestUsedIndexY = BoxTop - NEIGHBORHOOD_RADIUS_FOR_RECOGNIZING_MARKED_PIXEL;
	int SmallestUsedIndexX = BoxLeft - NEIGHBORHOOD_RADIUS_FOR_RECOGNIZING_MARKED_PIXEL;
	int LargestUsedIndexY  = BoxBottom + NEIGHBORHOOD_RADIUS_FOR_RECOGNIZING_MARKED_PIXEL;
	int LargestUsedIndexX  = BoxRight + NEIGHBORHOOD_RADIUS_FOR_RECOGNIZING_MARKED_PIXEL;
	int MaxLegalImageIndexY = (ImageHeight-1);
	int MaxLegalImageIndexX = (ImageWidth-1);

	if	(	( SmallestUsedIndexY < 0 ) ||
			( SmallestUsedIndexX < 0 ) ||
			( LargestUsedIndexY > MaxLegalImageIndexY ) ||
			( LargestUsedIndexX > MaxLegalImageIndexX ) )
	{
		return FALSE;
	}
	// If the pixels at all 4 corners of the box are "marked pixels", then this
	// contains a face.
	NumOfMarkedCorners = 0;
	for ( CornerInd = 0; CornerInd < NUM_OF_CORNERS; CornerInd++ )
	{
		if ( IsMarkedPixel( I, Corners_y[CornerInd], Corners_x[CornerInd] ) )
			NumOfMarkedCorners++;
	}

	return ( NumOfMarkedCorners >= MINIMUM_REQUIRED_MARKED_CORNERS );

	#undef NUM_OF_CORNERS 	
	#undef MINIMUM_REQUIRED_MARKED_CORNERS 
}

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

static BOOL IsMarkedPixel( IspImage I, int y, int x )
{
	// A marked pixel is a pixel whose value equals the average of the values of 
	// its neighbors, minus a special value called ENCODING_OFFSET.

	const int ENCODING_OFFSET  = 1;
	const int NUM_OF_NEIGHBORS = 8;

	int EncodedPixelValue;
	int dx;
	int dy;
	int AverageOfNeighbors;
	int SumOfNeighbors = 0;

	#if 1
	Byte_t Iyx;
#endif
	
	for ( dy = -1; dy <=1 ; dy++ )
	{
		for ( dx = -1; dx <=1 ; dx++ )
		{
			if ( (dx == 0) && (dy == 0) )
				continue;

			SumOfNeighbors += I[y+dy][x+dx];
		}
	}

	AverageOfNeighbors = SumOfNeighbors / NUM_OF_NEIGHBORS;

	EncodedPixelValue = (AverageOfNeighbors - ENCODING_OFFSET );

#if 1
	Iyx = I[y][x];
#endif 
	
	return ( I[y][x] == EncodedPixelValue );
}